//! Шрифт
class Font {
protected:

	GLuint _vbo;
	
	//! Шейдер рендера шрифта
	static class FontShaderProgram : public ShaderProgram{
	
	friend class Font;
	
	protected:
	
		struct Location {
			int projection_matrix;
			int color;
		} location;
	
	} _shader;

	//! Шрифтовая библиотека
	static FT_Library _library;

	//! Шрифт
	FT_Face _face;

	//! Размер шрифта
	int _size;
	
	//! Класс для хранения текстур глифов
	class Glyph {
	
	friend class Font;
	
	protected:
		
		GLuint _texture;
		
	public:
	
		Glyph(){
			_texture = 0;
		}

		
		~Glyph(){
			glDeleteTextures(1,&_texture);
		}
		
	};
	
	typedef map <unsigned int,Glyph> GlyphMap;
	
	GlyphMap _glyph;
	
public:

	vec4 color;

	float cursor_x;

	float cursor_y;
	
	void print_mode(){
		_shader.use();
		int vport[4];
		glGetIntegerv(GL_VIEWPORT,vport);
		mat4 m = ortho(0.0f,(float)vport[2],(float)vport[3],0.0f,0.0f,1.0f);
		_shader.uniform_matrix(_shader.location.projection_matrix,m);
		_shader.uniform_vec4(_shader.location.color,color);
		
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glDisable(GL_ALPHA_TEST);
	}
	
	void put_string(const wchar_t* str){
		if (_face) {
			int vport[4];
			glGetIntegerv(GL_VIEWPORT,vport);
			while(*str){
				switch(*str){
				case L'\n':
					cursor_x = 0;
					cursor_y+=_size;
					str++;
					break;
				default:
					FT_GlyphSlot slot = _face->glyph;
					FT_UInt glyph_index = FT_Get_Char_Index(_face,*str);
					if (glyph_index) {
						GlyphMap::iterator it = _glyph.find(*str);
						
						if (it == _glyph.end()){
							if (FT_Load_Glyph(_face,glyph_index,FT_LOAD_DEFAULT))
								return;
							if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL ))
								return;
							_glyph[*str];
							it = _glyph.find(*str);
				
							unsigned char* data = (unsigned char*)malloc(slot->bitmap.width*slot->bitmap.rows*4);
							unsigned char* dst = data;
							
							unsigned char* ssrc = slot->bitmap.buffer;
							
							for (int j = 0; j!=slot->bitmap.rows;j++){
								unsigned char* src = ssrc;
								for (int i = 0;i!=slot->bitmap.width;i++){
									*dst = *src; dst++;
									*dst = *src; dst++;
									*dst = *src; dst++;
									*dst = *src; dst++;
									src++;
								}
								ssrc+=slot->bitmap.pitch;
							}
							glGenTextures(1,&((*it).second._texture));
							glBindTexture(GL_TEXTURE_2D,(*it).second._texture);
							glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
							glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
							glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,slot->bitmap.width,slot->bitmap.rows,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
							free(data);
						} else {
							FT_Load_Glyph(_face,glyph_index,FT_LOAD_RENDER);
							glBindTexture(GL_TEXTURE_2D,(*it).second._texture);
						}
						
						int adv = slot->advance.x >> 6;
						
						if (cursor_x+adv >= vport[2]) {
							cursor_x = 0;
							cursor_y += _size;
						}

						float x = cursor_x+slot->bitmap_left;
						float y = (cursor_y+_size)-slot->bitmap_top;
						float x1 = x+slot->bitmap.width;
						float y1 = y+slot->bitmap.rows;
						
						x=0;y=0;x1=100;y1=100;	
						
						float position[16] = {
							x,y1,0,1,
							x1,y1,1,1,
							x,y,0,0,
							x1,y,1,0
						};

						glBindBuffer(GL_ARRAY_BUFFER,_vbo);
						glBufferData(GL_ARRAY_BUFFER,sizeof(float)*16,position,GL_STATIC_DRAW);
						glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*4,(const GLvoid*)0); 
						glEnableVertexAttribArray(0);
						glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(float)*4,(const GLvoid*)(sizeof(float)*2)); 
						glEnableVertexAttribArray(1);
						
						glDrawArrays(GL_TRIANGLE_STRIP,0,4);
						
						glDisableVertexAttribArray(1);
						glDisableVertexAttribArray(0);
						
						cursor_x += adv;
					}
					str++;
				}
			}
		}
	}

	//! Вернет размер шрифта
	const int size() const{
		return _size;
	}

	//! Установить размер
	void set_size(int sz){
		_size = sz;
		if (_face) 
			FT_Set_Pixel_Sizes(_face,0,sz);
	}

	//! Загрузить шрифт из файла
	bool load(const char* file_name){
		if (_face)
			FT_Done_Face(_face);
		if (FT_New_Face(_library,file_name,0,&_face)){
			_face = 0;
			return false;
		}
		set_size(_size);
		return true;
	}

	//! Конструктор
	Font(){
		glGenBuffers(1,&_vbo);
		_size = 18;
		_face = 0;
		if (!_library) {
			FT_Init_FreeType(&_library);
			// загрзука шейдера
			_shader.vertex_shader.load_file("data/shader/font.vertex");
			if (!_shader.vertex_shader.compile_status())
				cout << "font.vertex\n" << _shader.vertex_shader.error_string();
			_shader.fragment_shader.load_file("data/shader/font.fragment");
			if (!_shader.fragment_shader.compile_status())
				cout << "font.fragment\n" << _shader.fragment_shader.error_string();
			_shader.link();
			if (!_shader.link_status())
				cout << "font.program link error\n" << _shader.error_string();
			_shader.use();
			_shader.location.projection_matrix = _shader.get_uniform_location("projection_matrix");
			_shader.location.color = _shader.get_uniform_location("color");
		}
		cursor_x = 0;
		cursor_y = 0;
		color = vec4(1,1,1,1);
	}
	
	//! Деструктор
	~Font(){
		if (_face)
			FT_Done_Face(_face);
		glDeleteBuffers(1,&_vbo);
	}

};

FT_Library Font::_library;
Font::FontShaderProgram Font::_shader;